package cn.yeziji.forum.utils;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程池配置工具类
 *
 * @author gzkemays
 * @since 2021/7/19 11:37
 */
public class ThreadPoolConfigUtils {
  private int corePoolSize;
  private int maximumPoolSize;
  private long keepAliveTime;
  private TimeUnit timeUnit;
  private TimeUnit waitStopUnit;
  Future<?> future;
  private ThreadFactory threadFactory;
  ThreadPoolExecutor threadPoolExecutor;
  private List<Future> list;

  @FunctionalInterface
  public interface CycleOperation {
    /**
     * 自定义线程操作
     *
     * @param threadPoolExecutor 线程池
     * @return {@link Future} Callable 执行结果
     * @throws ExecutionException 执行 callable 异常
     * @throws InterruptedException 中断 shutdown 异常
     */
    void operation(ThreadPoolExecutor threadPoolExecutor)
        throws ExecutionException, InterruptedException;
  }

  /**
   * 构建线程池配置工厂
   *
   * <p>构建时如果需要自定义参数，则要在调用 {@link ThreadPoolConfigFactoryBuilder#createThreadPool()}
   * 前进行配置。否则配置无效采取默认配置参数。
   *
   * <p>在采取定时任务时，建议打开 {@link ThreadPoolConfigFactoryBuilder#openAllowCoreThread(int, TimeUnit)}
   * 对无任务时的线程池进行清理
   *
   * @return {@link ThreadPoolConfigUtils}
   */
  public static ThreadPoolConfigFactoryBuilder builder() {
    return new ThreadPoolConfigFactoryBuilder();
  }

  public static final class ThreadPoolConfigFactoryBuilder {
    private int corePoolSize = 5;
    private int maximumPoolSize = 10;
    private boolean allowCoreThread = false;
    private long keepAliveTime = 5L;
    private TimeUnit timeUnit = TimeUnit.MILLISECONDS;
    private final AtomicInteger threadIndex = new AtomicInteger();
    private ThreadFactory threadFactory =
        r -> new Thread(r, "线程池线程 -- " + threadIndex.getAndIncrement());
    private int waitStopTime = 10;
    private TimeUnit waitStopUnit = TimeUnit.SECONDS;
    private ThreadPoolExecutor threadPoolExecutor;
    private Future<?> future;
    private List<Future> list;

    /** 创建线程池 */
    public ThreadPoolConfigFactoryBuilder createThreadPool() {
      long keepLiveTime;
      TimeUnit keepLiveTimeUnit;
      if (allowCoreThread) {
        keepLiveTime = this.waitStopTime;
        keepLiveTimeUnit = this.waitStopUnit;
      } else {
        keepLiveTime = this.keepAliveTime;
        keepLiveTimeUnit = this.timeUnit;
      }
      ThreadPoolExecutor threadPoolExecutor =
          new ThreadPoolExecutor(
              this.corePoolSize,
              this.maximumPoolSize,
              keepLiveTime,
              keepLiveTimeUnit,
              new LinkedBlockingQueue<>(),
              this.threadFactory);
      // 是否线程池没任务时进行销毁
      threadPoolExecutor.allowCoreThreadTimeOut(allowCoreThread);
      this.threadPoolExecutor = threadPoolExecutor;
      return this;
    }

    private ThreadPoolConfigFactoryBuilder() {}

    public ThreadPoolConfigFactoryBuilder openAllowCoreThread(int waitTime, TimeUnit waitStopUnit) {
      this.waitStopTime = waitTime;
      this.waitStopUnit = waitStopUnit;
      this.allowCoreThread = true;
      return this;
    }

    public ThreadPoolConfigFactoryBuilder corePoolSize(int corePoolSize) {
      this.corePoolSize = corePoolSize;
      return this;
    }

    public ThreadPoolConfigFactoryBuilder maximumPoolSize(int maximumPoolSize) {
      this.maximumPoolSize = maximumPoolSize;
      return this;
    }

    public ThreadPoolConfigFactoryBuilder keepAliveTime(long keepAliveTime) {
      this.keepAliveTime = keepAliveTime;
      return this;
    }

    public ThreadPoolConfigFactoryBuilder timeUnit(TimeUnit timeUnit) {
      this.timeUnit = timeUnit;
      return this;
    }

    public ThreadPoolConfigFactoryBuilder threadFactory(ThreadFactory threadFactory) {
      this.threadFactory = threadFactory;
      return this;
    }

    public ThreadPoolConfigFactoryBuilder waitStopTime(int waitStopTime) {
      this.waitStopTime = waitStopTime;
      return this;
    }

    private ThreadPoolConfigFactoryBuilder waitStopUnit(TimeUnit waitStopUnit) {
      this.waitStopUnit = waitStopUnit;
      return this;
    }

    public ThreadPoolConfigFactoryBuilder noWait() {
      this.waitStopTime(0);
      return this;
    }

    public ThreadPoolConfigFactoryBuilder operation(CycleOperation cycleOperation) {
      try {
        cycleOperation.operation(this.threadPoolExecutor);
      } catch (ExecutionException | InterruptedException e) {
        e.printStackTrace();
      } finally {
        Runtime.getRuntime()
            .addShutdownHook(new Thread(() -> shutdownThread(this.threadPoolExecutor)));
      }
      return this;
    }

    /**
     * 关闭线程池
     *
     * @param executor 线程池
     */
    private void shutdownThread(ThreadPoolExecutor executor) {
      // 停止接受新任务
      executor.shutdown();
      try {
        // 等待终止指令
        if (!executor.awaitTermination(this.waitStopTime, this.waitStopUnit)) {
          executor.shutdownNow();
        }
      } catch (InterruptedException e) {
        executor.shutdownNow();
        // 通知阻塞线程结束进行中断
        Thread.currentThread().interrupt();
      }
    }

    public ThreadPoolConfigUtils build() {
      ThreadPoolConfigUtils threadPoolConfigFactory = new ThreadPoolConfigUtils();
      threadPoolConfigFactory.future = this.future;
      threadPoolConfigFactory.waitStopUnit = this.waitStopUnit;
      threadPoolConfigFactory.corePoolSize = this.corePoolSize;
      threadPoolConfigFactory.keepAliveTime = this.keepAliveTime;
      threadPoolConfigFactory.timeUnit = this.timeUnit;
      threadPoolConfigFactory.maximumPoolSize = this.maximumPoolSize;
      threadPoolConfigFactory.threadFactory = this.threadFactory;
      threadPoolConfigFactory.threadPoolExecutor = this.threadPoolExecutor;
      threadPoolConfigFactory.list = this.list;
      return threadPoolConfigFactory;
    }
  }

  public int getCorePoolSize() {
    return corePoolSize;
  }

  public void setCorePoolSize(int corePoolSize) {
    this.corePoolSize = corePoolSize;
  }

  public int getMaximumPoolSize() {
    return maximumPoolSize;
  }

  public void setMaximumPoolSize(int maximumPoolSize) {
    this.maximumPoolSize = maximumPoolSize;
  }

  public long getKeepAliveTime() {
    return keepAliveTime;
  }

  public void setKeepAliveTime(long keepAliveTime) {
    this.keepAliveTime = keepAliveTime;
  }

  public TimeUnit getTimeUnit() {
    return timeUnit;
  }

  public void setTimeUnit(TimeUnit timeUnit) {
    this.timeUnit = timeUnit;
  }

  public TimeUnit getWaitUnit() {
    return waitStopUnit;
  }

  public void setWaitUnit(TimeUnit waitStopUnit) {
    this.waitStopUnit = waitStopUnit;
  }

  public Future<?> getFuture() {
    return future;
  }

  public ThreadPoolExecutor getThreadPoolExecutor() {
    return threadPoolExecutor;
  }

  public ThreadFactory getThreadFactory() {
    return threadFactory;
  }
}
